
#include "zen_metal.id.h"
#include "zen_metal_type.h"

namespace Zen { namespace Metal {
	Render * Render::Get()
	{
		static auto single = new Render;
		return single;;
	}

	Render::Render()
	{
		m_id = new RenderID;
	}
	Render::~Render()
	{
		delete m_id;
	}

	RenderID * Render::getID() const
	{
		return m_id;
	}
	void Render::start()
	{

		m_id->descritptor = Device::Get()->getID()->view.currentRenderPassDescriptor;

		/// bg color.
		auto & c = m_clear_color;
		m_id->descritptor.colorAttachments[0].clearColor
		= MTLClearColorMake(c.red, c.green, c.blue, c.alpha);

		/// depth. stencil,
		m_id->descritptor.depthAttachment.clearDepth = (double)m_depth_clear;
		m_id->descritptor.stencilAttachment.clearStencil = 0;

		/// create.
		m_id->command_buffer = Device::Get()->getID()->command_queue.commandBuffer;
		m_id->encoder = [m_id->command_buffer renderCommandEncoderWithDescriptor:m_id->descritptor];
		m_id->encoder.label = @"ZenRenderEncoder";

		/// face.
		[m_id->encoder setFrontFacingWinding:(m_clockwise_as_front?MTLWindingClockwise: MTLWindingCounterClockwise)];

		[m_id->encoder setCullMode:(MTLCullMode)(int)m_cull_face];

	}

	void Render::end()
	{
		[m_id->encoder endEncoding];
		[m_id->command_buffer presentDrawable:Device::Get()->getID()->view.currentDrawable];
		[m_id->command_buffer commit];
		[m_id->command_buffer waitUntilCompleted];

		m_id->command_buffer = nil;
		m_id->encoder = nil;
		m_id->descritptor = nil;
	}

	void Render::activeEffect(std::shared_ptr<ShaderKitID> pipe_id)
	{
		[m_id->encoder setRenderPipelineState:pipe_id->state];

//		MTLDepthStencilDescriptor *depthDescriptor = [MTLDepthStencilDescriptor new];
//		depthDescriptor.depthCompareFunction = MTLCompareFunctionLessEqual;
//		depthDescriptor.depthWriteEnabled = NO;
//
//		auto device = Device::Get()->getID()->device;
//		[m_id->encoder setDepthStencilState:[device newDepthStencilStateWithDescriptor:depthDescriptor]];
//		[m_id->encoder st]
	}

	void Render::setVertexBuffer(int index, std::shared_ptr<BufferID> buffer, size_t off)
	{
		[m_id->encoder setVertexBuffer:(buffer? buffer->data : nil)
								offset:(NSUInteger)off
							   atIndex:(NSUInteger)index];
	}

	void Render::setFragmentBuffer(int index, std::shared_ptr<BufferID> buffer, size_t off)
	{
		[m_id->encoder setFragmentBuffer: (buffer? buffer->data : nil)
								  offset:(NSUInteger)off
								 atIndex:(NSUInteger)index];
	}

	void Render::setFragmentBytes(int index, const void *bytes, size_t len)
	{
		[m_id->encoder setFragmentBytes:bytes length:(NSInteger)len atIndex:(NSInteger)index];
	}
	void Render::bindTexture(int index, std::shared_ptr<TextureID> texture)
	{
		[m_id->encoder setFragmentTexture:(texture? texture->mtl_texture : nil)
								  atIndex:(NSUInteger)index];
	}

	void Render::drawPrimitives(eVMode mode, size_t start, size_t count)
	{
		[m_id->encoder drawPrimitives:(MTLPrimitiveType)mode
						  vertexStart:(NSUInteger)start
						  vertexCount:(NSUInteger)count];
	}

	void Render::drawIndexedU32Primitives(eVMode mode, size_t count, std::shared_ptr<BufferID> buffer, size_t off_count)
	{
		[m_id->encoder drawIndexedPrimitives:(MTLPrimitiveType)mode
								  indexCount:count
								   indexType:(MTLIndexTypeUInt32)
								 indexBuffer:buffer->data
						   indexBufferOffset:0
							   instanceCount:0];

	}

	void Render::drawIndexedU16Primitives(eVMode mode, size_t count, std::shared_ptr<BufferID> buffer, size_t off_count)
	{

		[m_id->encoder drawIndexedPrimitives:(MTLPrimitiveType)mode
								  indexCount:count
								   indexType:(MTLIndexTypeUInt16)
								 indexBuffer:buffer->data
						   indexBufferOffset:0
							   instanceCount:0];
	}
	void Render::drawIndexedPrimitives(eVMode mode, size_t count, std::shared_ptr<BufferID> buffer, size_t off_count, size_t step)
	{
		assert((step == 2 || step == 4));

		[m_id->encoder drawIndexedPrimitives:(MTLPrimitiveType)mode
								  indexCount:count
								   indexType:(step == 4?MTLIndexTypeUInt32:MTLIndexTypeUInt16)
								 indexBuffer:buffer->data
						   indexBufferOffset:0
							   instanceCount:1];
	}

	
	/// face.
	void Render::setClockwiseAsFront(bool v) {
		m_clockwise_as_front = v;
		if(m_id->encoder)
		{
			[m_id->encoder setFrontFacingWinding:(m_clockwise_as_front?MTLWindingClockwise: MTLWindingCounterClockwise)];
		}
	}
	void Render::cullFace(eFace face) {
		m_cull_face = face;
		if(m_id->encoder)
		{
			[m_id->encoder setCullMode:(MTLCullMode)(int)m_cull_face];
		}
	}
	void Render::enableDepthTest(bool v) {
		m_depth_test = v;
		if(m_id->encoder)
		{
//			[m_id->descritptor
//			[m_id->encoder setDepthStoreAction:(MTLStoreActionStore)];
		}
	}
	void Render::setClearDepth(float z) {
		m_depth_clear = z;
		//		desc.stencilAttachment.clearStencil = 0;

	}
	void Render::setDepthMask(bool on) {

	}
	bool Render::isDepthTestEnabled() {
		return m_depth_test;
	}

	void Render::setClearColor(Color4f const & c)
	{
		m_clear_color = c;

		/**
		if(m_id->descritptor)
		{
			m_id->descritptor.colorAttachments[0].clearColor
			= MTLClearColorMake(c.red, c.green, c.blue, c.alpha);
		}
		 */
	}

}}
